/*
 * ymodem.h
 *
 *  Created on: Dec 25, 2023
 *      Author: jetpo
 */

#ifndef INC_YMODEM_H_
#define INC_YMODEM_H_

#include "stm32f1xx_hal.h"
#include "stm32f1xx_hal_uart.h"
#include <stdint.h>

/* Exported constants --------------------------------------------------------*/
/* Packet structure defines */
#define PACKET_HEADER_SIZE ((uint32_t)3)
#define PACKET_DATA_INDEX ((uint32_t)3)
#define PACKET_START_INDEX ((uint32_t)0)
#define PACKET_NUMBER_INDEX ((uint32_t)1)
#define PACKET_CNUMBER_INDEX ((uint32_t)2)
#define PACKET_TRAILER_SIZE ((uint32_t)2)
#define PACKET_OVERHEAD_SIZE (PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE) /* 帧头+帧尾的长度 */
#define PACKET_DATA_SIZE ((uint32_t)128)                                /* 纯128字节包的数据长度  */
#define PACKET_DATA_1K_SIZE ((uint32_t)1024)                            /* 纯1024字节包的数据长度 */

#define FILE_NAME_LENGTH ((uint32_t)64) /* 文件名最大只支持64位     */
#define FILE_SIZE_LENGTH ((uint32_t)16) /* 文件长度字最大只支持16位 */

/*
   SENDER                                               RECEIVER
   ======                                               ========
                                                           C
                                                           C
   SOH 00 FF Filename NULs CRC CRC
                                                          ACK
                                                           C
   STX 01 FD Data[1024] CRC1 CRC2
                                                          ACK
   SOH 02 FC Data[128] CRC1 CRC2
                                                          ACK
   SOH 03 FB Data[100] ^Z[28] CRC1 CRC2
                                                          ACK
   EOT
                                                          NAK
   EOT
                                                          ACK
                                                           C
   SOH 00 FF NUL[128] CRC CRC
                                                          ACK
 */

/**
 * ymodem时序
 *
 * SENDER                            RECEVIER
 *                                        'C'(发送端一直等待这个字符)
 * 文件信息桢 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 *                                         ACK             ACK和'C'合起来发即为06 43
 *                                         'C'(0x43)
 * 文件数据帧 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 *                                         ACK(接收正确, 0x06)
 * 文件数据帧 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 *                                         NAK(接收失败, 0x15)
 * 文件数据帧 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 *                                         ACK(接收正确, 0x06)
 *
 * EOT(0x04) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 *                                         NAK(结束确认, 0x15)
 * EOT(0x04) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 *                                         ACK(结束确认, 0x06)
 *                                         'C'(0x43)
 * SOH 0x00 0xFF NULL NULL CRC_H CRCL >>>>>>>>>>
 *                                          ACK(结束确认, 0x06)
 * 通信结束
 */

/* /-------- Packet Struct ---------------------------------------------\
 * | 0      |  1    |  2     |  3      |   ...  | n+3     | n+4  | n+5  |
 * |--------------------------------------------------------------------|
 * | start  | number| !num   | data[0] |   ...  | data[n] | crcH | crcL |
 * \--------------------------------------------------------------------/
 * 其中
 * - n长度为1024或者128
 * - 文件信息帧实测有可能是1024帧(使用了SecureCRT进行文件传输实测结果)
 * - 最后的"1包"超过了128字节可能是2个128帧, 而不是1个1024帧(使用了SecureCRT进行文件传输实测结果)
 *
 * */

#define SOH ((uint8_t)0x01) /* start of 128-byte data packet */
#define STX ((uint8_t)0x02) /* start of 1024-byte data packet */
#define EOT ((uint8_t)0x04) /* end of transmission */
#define CA ((uint8_t)0x18)  /* two of these in succession aborts transfer */

#define PACKET_TIMEOUT (0x500)
#define MAX_ERROR (5)

typedef enum
{
   YMODEM_StopWithError,      /* 出错退出 */
   YMODEM_AbortBySender,      /* 发送段主动停止 */
   YMODEM_ReceiveSuccess,     /* 接收成功 */
   YMODEM_WaittingConnection, /* 接收端发'C'等待发送端响应 */
   YMODEM_InfoReceived,       /* 文件信息包已接收 */
   YMODEM_FileReceiving,      /* 文件包接收中 */
   YMODEM_EOTReceivedOnce,    /* 接收到一次EOT */
   YMODEM_WaittingEndPacket,  /* 接收到2次EOT后等待结束包 */
} YmodemReceiveSession;       /* ymodem通信状态字 */

typedef enum
{
   YMODEM_Timeout,
   YMODEM_ErrorPacket,
   YMODEM_InfoPacket,
   YMODEM_FilePacket,
   YMODEM_CAPacket,
   YMODEM_EOTPacket,
   YMODEM_EndPacekt,
} YmodemReceiveType; /* ymodem收包类型字 */

/**
 * @brief ymodem通信流程
 *
 * @param p_session         通信阶段, 外部应该赋予初始值为WaittingConnection, 在内部会被更新
 * @param pUart             通信端口
 * @param timeout           单次接收超时(ms)
 * @param pout_dat          当前接收到的数据
 * @param pout_size         当前接收到的有效数据长度128/1024
 * @param pout_filename_len 接收的文件名
 * @param pout_filesize     接收到的文件长度
 */
YmodemReceiveType ymodem_comm(YmodemReceiveSession *p_session,
                              UART_HandleTypeDef *pUart,
                              uint16_t timeout,
                              uint8_t *pout_dat,
                              uint32_t *pout_size,
                              char *pout_filename,
                              uint16_t *pout_filename_len,
                              uint32_t *pout_filesize);

/**
 * @brief 处理1个完整的数据信息包, 不负责校验部分
 *        文件信息包格式
 *        SOH 00 0xFF FILE_NAME '\0' FILE_LENGHT ' ' [用SecureCRT发的乱码未考证为什么， 按照协议应该是填0x00?] 0x00 .. 0x00 crc_hight crc_low
 *        其中
 *           - 文件长度使用字符串表示
 *           - 文件名结尾有1个符号'\0', 即0x00
 *           - 长度字符串结尾有1个' '(空格符), 再往后目前对我来看是乱码
 *
 * @param p_buf             原始数据流
 * @param pout_filename     返回值:解码出的文件名
 * @param pout_filename_len 返回值:解码出的文件名实际长度, 包含最后的0x00
 * @param pout_filelsize    返回值:解码出的文件长度
 * @return  0:解码成功;
 *          其它:解码失败.
 */
int ymodem_parse_fileinfo(uint8_t *p_buf, char *pout_filename, uint16_t *pout_filename_len, uint32_t *pout_filesize);

#endif /* INC_YMODEM_H_ */
